/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../objects/head.h"
#include "../impl/mgr.h"
#include "misc_impl.h"
#include <algorithm>
#include <float.h>
#include <limits>
#include <cmath>

namespace xos_common
{
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static misc_impl::ITEM_DATA xos_item_data;

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 
    misc_impl::misc_impl()
    {}

    misc_impl::~misc_impl()
    {}

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int misc_impl::get_xos_item_data( ITEM_DATA*& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //  

    int misc_impl::init()
    {
        int nRet = 0;
        return nRet;
    }

    int misc_impl::term()
    {
        int nRet = 0;
        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 
    // i_misc method

    // 以指定进制显示数据内容 : 16, 10, 8
    int misc_impl::bin_to_text( char* lpszOutData, int nOutLen, int* pnOutLen, const unsigned char* lpszInData, int nInLen, int decimal )
    {
        int ret = 0;

        int nLen = 0, nPos = 0;
        char fmt[64] = { 0 };

        switch( decimal )
        {
            case 16:
            {
                mgr::container()->crt()->strcpy( fmt, sizeof( fmt ), "%02x" );
            }
            break;
            case 10:
            {
                mgr::container()->crt()->strcpy( fmt, sizeof( fmt ), "%02d" );
            }
            break;
            case 8:
            {
                mgr::container()->crt()->strcpy( fmt, sizeof( fmt ), "%02o" );
            }
            break;
            default:
            {
                ret = 1;
            }
            break;
        }

        while( (0 == ret) && (nOutLen > nLen) && (nInLen > nPos) )
        {
            int len = mgr::container()->crt()->sprintf( lpszOutData + nLen, nOutLen - nLen, fmt, (int)lpszInData[nPos] );
            nLen += len;
            nPos += 1;
        }

        if( (0 == ret) && pnOutLen )
        {
            *pnOutLen = nLen;
        }

        return ret;
    }

    // 
    // 3des
    // 
    int misc_impl::DoDES( char* Out, const char* In, long datalen, const char* Key, int keylen, bool bEncode )
    {
        int nRet = 0;
        des tool;
        nRet = tool.DoDES( Out, In, datalen, Key, keylen, bEncode );
        return nRet;
    }

    // 
    // crc16
    // 
    unsigned short misc_impl::cal_crc16( const unsigned char* lpData, int nLen )
    {
        return xos_common::cal_crc16( lpData, nLen );
    }

    // 
    // url encode / decode
    // 
    int misc_impl::url_encode( const char* lpszSrc, int nSrcLen, char* lpszDst, int nCharNum )
    {
        int nRet = 0;
        nRet = xos_common::url_encode( lpszSrc, nSrcLen, lpszDst, nCharNum );
        return nRet;
    }

    int misc_impl::url_decode( const char* lpszSrc, int nSrcLen, char* lpszDst, int nCharNum )
    {
        int nRet = 0;
        nRet = xos_common::url_decode( lpszSrc, nSrcLen, lpszDst, nCharNum );
        return nRet;
    }

    // 
    // base64 encode / decode
    // 
    int misc_impl::base64_encode( const char* lpszSrc, int nSrcLen, char* lpszDst, int nCharNum )
    {
        int nRet = 0;

        int nLen = nSrcLen;
        const char* lpszEncode = 0;
        base64 tool;

        tool.Encode( lpszSrc, nLen );
        lpszEncode = tool.EncodedMessage();

        mgr::container()->crt()->strcpy( lpszDst, nCharNum, lpszEncode );

        return nRet;
    }

    int misc_impl::base64_decode( const char* lpszSrc, int nSrcLen, char* lpszDst, int nCharNum )
    {
        int nRet = 0;

        int nLen = nSrcLen;
        const char* lpszDecode = 0;
        base64 tool;

        tool.Decode( lpszSrc, nLen );
        lpszDecode = tool.DecodedMessage();

        mgr::container()->crt()->strcpy( lpszDst, nCharNum, lpszDecode );

        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // 计算分页的url
    // 
    int misc_impl::page_info( int nMaxPage, int nPageNum, int nCurPage, bool bEnable, char* lpszPageInfoUrl, int nBufSize, int* lpnInfoSize )
    {
        int ret = 0;

        int max_num = nMaxPage;
        int begin = 0, end = 0;

        {
            int half = max_num / 2;
            int b = nCurPage - half;
            int e = 0;
            if( b < 0 )
            {
                b = 0;
            }
            e = b + max_num;
            if( e > nPageNum )
            {
                e = nPageNum;
            }
            begin = b;
            end = e;
        }

        std::string fmt_cur_link = "<a href=%d style='text-decoration:underline;'>&lt;%d&gt;</a>";
        std::string fmt_link = "<a href=%d>&lt;%d&gt;</a>";
        std::string style_link = "<style>a:link,a:visited{text-decoration:none;}</style>";

        std::string fmt_cur_none = "<span style='text-decoration:underline;'>&lt;%d&gt;</span>";
        std::string fmt_none = "<span>&lt;%d&gt;</span>";
        std::string style_none = "<style>{text-decoration:none;}</style>";

        std::string* pStyle = &style_link;
        std::string* pFmtCur = &fmt_cur_link;
        std::string* pFmt = &fmt_link;

        if( !bEnable )
        {
            pFmtCur = &fmt_cur_none;
            pStyle = &style_none;
            pFmt = &fmt_none;
        }

        std::string content = *pStyle;

        for( int i = begin; i < end; ++i )
        {
            const std::string* pFormat = pFmt;
            char buf[4096] = { 0 };
            if( i == nCurPage )
            {
                pFormat = pFmtCur;
            }
            if( bEnable )
            {
                mgr::container()->crt()->sprintf( buf, sizeof( buf ), pFormat->c_str(), i, i + 1 );
            }
            else
            {
                mgr::container()->crt()->sprintf( buf, sizeof( buf ), pFormat->c_str(), i + 1 );
            }
            content += buf;
        }

        if( lpszPageInfoUrl && (nBufSize > (int)content.length()) )
        {
            mgr::container()->crt()->strcpy( lpszPageInfoUrl, nBufSize, content.c_str() );
        }

        if( lpnInfoSize )
        {
            *lpnInfoSize = (int)content.length();
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // 字串操作
    // 
    const char* misc_impl::split_full_file( const char* lpszFullFile, char* lpszPath, int nSize, int* lpnSize )
    {
        const char* pRet = 0;
        int nPathLen = 0;
        int ret = 0;

        if( 0 == ret )
        {
            const char* lpszW = mgr::container()->crt()->strrchr( lpszFullFile, '\\' );
            const char* lpszL = mgr::container()->crt()->strrchr( lpszFullFile, '/' );
            mgr::container()->crt()->strlen( lpszFullFile );
            pRet = lpszW > lpszL ? lpszW : lpszL;
            if( pRet )
            {
                pRet++;
                nPathLen = (int)(pRet - lpszFullFile);
            }
            else
            {
                ret = 1;
            }
        }

        if( (0 == ret) && lpszPath && (nSize > nPathLen) )
        {
            mgr::container()->crt()->strncpy( lpszPath, nSize, lpszFullFile, nPathLen );
        }

        if( (0 == ret) && lpnSize )
        {
            *lpnSize = nPathLen;
        }

        return pRet;
    }

    const char* misc_impl::ext( const char* lpszFile, xos_box::i_string * pStr )
    {
        xos_box::i_buf * pBuf = mgr::container()->box()->buf();
        char * lpszName = pBuf->get_data( 0,0,0,0 );
        int nSize = pBuf->get_buf_size(0);

        const char* pRet = 0;
        int nNameLen = 0;

        int ret = 0;

        if( 0 == ret )
        {
            pRet = mgr::container()->crt()->strrchr( lpszFile, '.' );
            if( pRet )
            {
                pRet++;
                nNameLen = (int)(pRet - lpszFile - 1);
            }
            else
            {
                ret = 1;
            }
        }

        if( (0 == ret) && lpszName && ( nNameLen > 0 ) && ( nSize > nNameLen ) )
        {
            mgr::container()->crt()->strncpy( lpszName, nSize, lpszFile, nNameLen );
            if( pStr )
            {
                pStr->set( lpszName );
            }
        }

        xos_stl::release_interface( pBuf );

        return pRet;
    }

    int misc_impl::parse_uri_to_property( const char* lpszUri, xos_box::i_int_property * pProp )
    {
        int ret = 0;

        xos::i_crt* pCrt = mgr::container()->crt();
        int nLen = pCrt->strlen( lpszUri );
        xos_box::i_buf * pBuf = mgr::container()->box()->buf();
        char * buf = pBuf->get_data( 0, 0, 0, 0 );
        size_t tSize = pBuf->get_buf_size(0);
        char * lpszPath = buf;
        int nPathIndex = 0;

        {
            pCrt->strncpy( buf, tSize, lpszUri, nLen );
        }

        for( int i = 0; i < nLen; ++i )
        {
            if( buf[i] == '/' )
            {
                buf[i] = 0;
            }
            else if( i != nLen - 1 )
            {
                continue;
            }
            if( 0 != *lpszPath )
            {
                pProp->set( nPathIndex, lpszPath );
                nPathIndex++;
            }
            lpszPath = buf + i + 1;
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    // \t -> \\t, ' -> \\', " -> \\"
    int misc_impl::trans( const char* lpszSrc, char* lpszDst, int nLen, int* lpnLen )
    {
        const char* pszSrc = lpszSrc;
        int nPos = 0;
        int ret = 0;
        char c = 0;

        while( (c = *pszSrc++) )
        {
            switch( c )
            {
                case '\"':
                case '\'':
                case '\\':
                {
                    if( nPos < nLen - 2 )
                    {
                        lpszDst[nPos + 0] = '\\';
                        lpszDst[nPos + 1] = c;
                    }
                    nPos += 2;
                }
                break;
                case '\t':
                {
                    if( nPos < nLen - 2 )
                    {
                        lpszDst[nPos + 0] = '\\';
                        lpszDst[nPos + 1] = 't';
                    }
                    nPos += 2;
                }
                break;
                case '\r':
                {
                    if( nPos < nLen - 2 )
                    {
                        lpszDst[nPos + 0] = '\\';
                        lpszDst[nPos + 1] = 'r';
                    }
                    nPos += 2;
                }
                break;
                case '\n':
                {
                    if( nPos < nLen - 5 )
                    {
                        lpszDst[nPos + 0] = '\\';
                        lpszDst[nPos + 1] = 'n';
                        lpszDst[nPos + 2] = '\\';
                        lpszDst[nPos + 3] = '\r';
                        lpszDst[nPos + 4] = '\n';
                    }
                    nPos += 5;
                }
                break;
                default:
                {
                    if( nPos < nLen - 1 )
                    {
                        lpszDst[nPos] = c;
                    }
                    nPos += 1;
                }
                break;
            }
        }

        if( nPos < nLen - 1 )
        {
            lpszDst[nPos] = 0;
        }
        else
        {
            ret = 1;
        }

        if( lpnLen )
        {
            *lpnLen = nPos;
        }

        return ret;
    }

    int misc_impl::begin_with( const char* lpszStr, const char* lpszSub )
    {
        int ret = 0;

        int str_len = mgr::container()->crt()->strlen( lpszStr );
        int sub_len = mgr::container()->crt()->strlen( lpszSub );

        if( (0 == ret) && (str_len < sub_len) )
        {
            ret = 1;
        }

        for( int i = 0; (0 == ret) && (i < sub_len); ++i )
        {
            if( lpszStr[i] != lpszSub[i] )
            {
                ret = 1;
                break;
            }
        }

        return ret;
    }

    int misc_impl::end_with( const char* lpszStr, const char* lpszSub )
    {
        int ret = 0;

        int str_len = mgr::container()->crt()->strlen( lpszStr );
        int sub_len = mgr::container()->crt()->strlen( lpszSub );

        const char* lpszStrSub = lpszStr + str_len - sub_len;

        if( (0 == ret) && (str_len < sub_len) )
        {
            ret = 1;
        }

        for( int i = 0; (0 == ret) && (i < sub_len); ++i )
        {
            if( lpszStrSub[i] != lpszSub[i] )
            {
                ret = 1;
                break;
            }
        }

        return ret;
    }

    int misc_impl::trim( char* lpszStr, const char* lpszTrims )
    {
        int ret = 0;

        if( !lpszStr || !lpszTrims || (0 == lpszStr[0]) || (0 == lpszTrims[0]) )
        {
            return 1;
        }

        int nLen = mgr::container()->crt()->strlen( lpszStr );

        char* lpszRight = lpszStr + nLen - 1;
        char* lpszLeft = lpszStr;

        while( *lpszLeft )
        {
            const char* lpszTrim = lpszTrims;
            while( *lpszTrim && (*lpszTrim != *lpszLeft) )
            {
                lpszTrim++;
            }
            if( *lpszTrim )
            {
                *lpszLeft++ = 0;
            }
            else
            {
                break;
            }
        }

        while( *lpszRight )
        {
            const char* lpszTrim = lpszTrims;
            while( *lpszTrim && (*lpszTrim != *lpszRight) )
            {
                lpszTrim++;
            }
            if( *lpszTrim )
            {
                *lpszRight-- = 0;
            }
            else
            {
                break;
            }
        }

        nLen = (int)(lpszRight - lpszLeft + 1);

        if( nLen > 0 )
        {
            if( lpszStr != lpszLeft )
            {
                mgr::container()->crt()->memmove( lpszStr, lpszLeft, nLen );
                lpszStr[nLen] = 0;
            }
        }
        else
        {
            lpszStr[0] = 0;
        }

        return ret;
    }

    int misc_impl::valid( const char* lpszStr, int nLen )
    {
        int ret = 0;
        int len = mgr::container()->crt()->strlen( lpszStr );
        if( len + 1 > nLen )
        {
            ret = 1;
        }
        return ret;
    }

    // split
    int misc_impl::str_split( char* lpszUrl, const char seperator )
    {
        int ret = 0;

        char* lpszStr = lpszUrl;

        if( !lpszStr )
        {
            return 1;
        }

        while( 0 != *lpszStr )
        {
            if( seperator == *lpszStr )
            {
                *lpszStr = 0;
            }
            lpszStr++;
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // 拷贝文件
    // 
    int misc_impl::copy( const char* lpszSrcFile, const char* lpszDstFile )
    {
        int ret = 0;

        xos::i_file* pSrc = 0, * pDst = 0;
        char buf[4096];
        int nLen = 0;

        if( 0 == ret )
        {
            pSrc = mgr::container()->xos()->file();
            pDst = mgr::container()->xos()->file();
            if( !pSrc || !pDst )
            {
                ret = 1;
            }
        }
        if( 0 == ret )
        {
            ret = pSrc->open( lpszSrcFile, xos::i_file::enumFileAccessFlag::XOS_FILE_READ, xos::i_file::enumFileShareFlag::XOS_SHARE_READ, xos::i_file::enumFileOpenFlag::XOS_FILE_OPEN );
        }
        if( 0 == ret )
        {
            ret = pDst->open( lpszDstFile, xos::i_file::enumFileAccessFlag::XOS_FILE_WRITE, xos::i_file::enumFileShareFlag::XOS_SHARE_READ, xos::i_file::enumFileOpenFlag::XOS_FILE_CREATE );
        }
        while( 0 == ret )
        {
            nLen = pSrc->read( buf, sizeof( buf ) );
            if( nLen > 0 )
            {
                pDst->write( buf, nLen );
            }
            else
            {
                break;
            }
        }

        xos_stl::release_interface( pDst );
        xos_stl::release_interface( pSrc );

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // 常用判断方法
    // 
    bool misc_impl::is_valid_email_addr( const char* lpszEmail )
    {
        using namespace xos_stl;

        CRegexpT< char > regexp( "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$" );
        bool bRet = false;

        MatchResult result = regexp.Match( lpszEmail );
        if( result.IsMatched() )
        {
            bRet = true;
        }

        return bRet;
    }

    bool misc_impl::is_valid_id_card( const char* lpszIdCard )
    {
        bool bRet = false;
        id_card obj;
        bRet = obj.is_valid_id_card( lpszIdCard );
        return bRet;
    }

    bool misc_impl::is_valid_phone( const char* lpszPhone )
    {
        bool bRet = true;
        int nPhoneLen = 0;
        if( bRet && !lpszPhone )
        {
            bRet = false;
        }
        if( bRet )
        {
            nPhoneLen = mgr::container()->crt()->strlen( lpszPhone );
            if( 11 != nPhoneLen )
            {
                bRet = false;
            }
        }
        for( int i = 0; bRet && (i < nPhoneLen); ++i )
        {
            if( !isdigit( lpszPhone[i] ) )
            {
                bRet = false;
                break;
            }
        }
        return bRet;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    bool misc_impl::approximately_equal( double a, double b, double epsilon )
    {
        return fabs( a - b ) <= epsilon;
    }

    bool misc_impl::essentially_equal( double a, double b, double epsilon )
    {
        return fabs( a - b ) <= ((fabs( a ) > fabs( b ) ? fabs( b ) : fabs( a )) * epsilon);
    }

    bool misc_impl::definitely_greater( double a, double b, double epsilon )
    {
        return (a - b) > ((fabs( a ) < fabs( b ) ? fabs( b ) : fabs( a )) * epsilon);
    }

    bool misc_impl::definitely_less( double a, double b, double epsilon )
    {
        return (b - a) > ((fabs( a ) < fabs( b ) ? fabs( b ) : fabs( a )) * epsilon);
    }

    // 
    // float double比较
    // 
    int misc_impl::double_compare( double a, double b )
    {
        int ret = 0;
        if( definitely_greater( a, b ) )
        {
            ret = 1;
        }
        else if( definitely_less( a, b ) )
        {
            ret = -1;
        }
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // endian
    // 
    uint64_t misc_impl::to_small_endian_64( uint64_t value )
    {
        uint64_t ret = value;
        if( !is_small_endian() )
        {
            ret = swap_endian_64( value );
        }
        return ret;
    }

    uint64_t misc_impl::to_big_endian_64( uint64_t value )
    {
        uint64_t ret = value;
        if( is_small_endian() )
        {
            ret = swap_endian_64( value );
        }
        return ret;
    }

    uint32_t misc_impl::to_small_endian_32( uint32_t value )
    {
        uint32_t ret = value;
        if( !is_small_endian() )
        {
            ret = swap_endian_32( value );
        }
        return ret;
    }

    uint32_t misc_impl::to_big_endian_32( uint32_t value )
    {
        uint32_t ret = value;
        if( is_small_endian() )
        {
            ret = swap_endian_32(value);
        }
        return ret;
    }

    uint64_t misc_impl::to_small_endian_64( double value )
    {
        xos::i_crt* pCrt = mgr::container()->crt();
        uint64_t uData = 0;
        uint64_t ret = 0;
        pCrt->memcpy( &uData, &value, sizeof( value ) );
        ret = to_small_endian_64( uData );
        return ret;
    }

    uint64_t misc_impl::to_big_endian_64( double value )
    {
        xos::i_crt* pCrt = mgr::container()->crt();
        uint64_t uData = 0;
        uint64_t ret = 0;
        pCrt->memcpy( &uData, &value, sizeof( value ) );
        ret = to_big_endian_64( uData );
        return ret;
    }

    uint32_t misc_impl::to_small_endian_32( float value )
    {
        xos::i_crt* pCrt = mgr::container()->crt();
        uint32_t uData = 0;
        uint32_t ret = 0;
        pCrt->memcpy( &uData, &value, sizeof( value ) );
        ret = to_small_endian_32( uData );
        return ret;
    }

    uint32_t misc_impl::to_big_endian_32( float value )
    {
        xos::i_crt* pCrt = mgr::container()->crt();
        uint32_t uData = 0;
        uint32_t ret = 0;
        pCrt->memcpy( &uData, &value, sizeof(value) );
        ret = to_big_endian_32( uData );
        return ret;
    }

    uint64_t misc_impl::swap_endian_64( uint64_t value )
    {
        uint64_t ret = ((value & 0xFF00000000000000) >> 56) |
            ((value & 0x00FF000000000000) >> 40) |
            ((value & 0x0000FF0000000000) >> 24) |
            ((value & 0x000000FF00000000) >> 8) |
            ((value & 0x00000000FF000000) << 8) |
            ((value & 0x0000000000FF0000) << 24) |
            ((value & 0x000000000000FF00) << 40) |
            ((value & 0x00000000000000FF) << 56);
        return ret;
    }

    uint32_t misc_impl::swap_endian_32( uint32_t value )
    {
        uint32_t ret = ((value >> 24) & 0xFF) |
            ((value >> 8) & 0xFF00) |
            ((value << 8) & 0xFF0000) |
            ((value << 24) & 0xFF000000);
        return ret;
    }

    bool misc_impl::is_small_endian()
    {
        bool bRet = 0;
        uint16_t test = 0x0001;
        bRet = (*reinterpret_cast<uint8_t*>(&test) == 0x01);
        return bRet;
    }

} // xos_common
